﻿#include "C3fdRenderWidget.h"
#include <vtkAxesActor.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkTextRepresentation.h>
#include <vtkProperty.h>
#include <vtkDataSetAttributes.h>
#include <vtkLookupTable.h>
#include <vtkScalarBarActor.h>
#include <vtkPointData.h>
#include <QPainter>
#include <QGridLayout>
#include <vtkCamera.h>
#include <vtkWindowToImageFilter.h>
#include <vtkImageResize.h>
#include <vtkJPEGWriter.h>
#include <vtkStructuredGridWriter.h>
#include <vtkDataSetWriter.h>
#include <QFileInfo>
#include <QDir>
#include <vtkPolyData.h>
#include "Settings/GraphOption.h"
#include "Settings/BusAPI.h"

namespace MainWidget
{
	VtkRenderWidget::VtkRenderWidget(QWidget *parent, Qt::WindowFlags f)
		: QVTKOpenGLNativeWidget(parent, f)
	{
	}

	void VtkRenderWidget::focusInEvent(QFocusEvent *event)
	{

		emit showBorderSig(true);
	}

	void VtkRenderWidget::focusOutEvent(QFocusEvent *event)
	{
		emit showBorderSig(false);
	}

	C3fdRenderWidget::C3fdRenderWidget(QWidget *parent, Qt::WindowFlags f)
		: QWidget(parent, f), m_VtkWidget(new VtkRenderWidget(parent, f))
	{
		setFocusPolicy(Qt::ClickFocus);
		m_RenderWindow = m_VtkWidget->renderWindow();
		m_Renderer = vtkSmartPointer<vtkRenderer>::New();
		m_RenderWindow->AddRenderer(m_Renderer);

		Setting::GraphOption *option = Setting::BusAPI::instance()->getGraphOption();
		QColor topcolor = option->getBackgroundTopColor();
		QColor bottomcolor = option->getBackgroundBottomColor();
		m_Renderer->SetBackground2(topcolor.redF(), topcolor.greenF(), topcolor.blueF());
		m_Renderer->SetBackground(bottomcolor.redF(), bottomcolor.greenF(), bottomcolor.blueF());
		m_Renderer->SetGradientBackground(true);
		addAxes();

		auto wLayout = new QGridLayout(this);
		wLayout->addWidget(m_VtkWidget);
		wLayout->setSpacing(0);
		wLayout->setMargin(0);
		this->setLayout(wLayout);
		this->setContentsMargins(2, 2, 2, 2);
		connect(m_VtkWidget, SIGNAL(showBorderSig(bool)), this, SLOT(showBorderSlot(bool)));
	}

	C3fdRenderWidget::~C3fdRenderWidget()
	{
		if (m_VtkWidget != nullptr)
		{
			delete m_VtkWidget;
			m_VtkWidget = nullptr;
		}
	}

	void C3fdRenderWidget::updateShadeField(QString field)
	{
		if (field.isEmpty())
		{
			return;
		}
		auto fields = getFields();
		auto pointFields = fields.value(Common::FieldLocation::Point);
		auto cellFields = fields.value(Common::FieldLocation::Cell);
		if (!pointFields.contains(field) && !cellFields.contains(field))
		{
			return;
		}

		auto lookupTable = vtkSmartPointer<vtkLookupTable>::New();
		lookupTable->Build();
		lookupTable->SetNumberOfColors(17);
		lookupTable->SetTableValue(0, 0, 0, 1);
		lookupTable->SetTableValue(1, 0, 0.25, 1);
		lookupTable->SetTableValue(2, 0, 0.5, 1);
		lookupTable->SetTableValue(3, 0, 0.75, 1);
		lookupTable->SetTableValue(4, 0, 1, 1);
		lookupTable->SetTableValue(5, 0, 1, 0.75);
		lookupTable->SetTableValue(6, 0, 1, 0.5);
		lookupTable->SetTableValue(7, 0, 1, 0.25);
		lookupTable->SetTableValue(8, 0, 1, 0);
		lookupTable->SetTableValue(9, 0.25, 1, 0);
		lookupTable->SetTableValue(10, 0.5, 1, 0);
		lookupTable->SetTableValue(11, 0.75, 1, 0);
		lookupTable->SetTableValue(12, 1, 1, 0);
		lookupTable->SetTableValue(13, 1, 0.75, 0);
		lookupTable->SetTableValue(14, 1, 0.5, 0);
		lookupTable->SetTableValue(15, 1, 0.25, 0);
		lookupTable->SetTableValue(16, 1, 0, 0);
		/*lookupTable->SetNumberOfColors(11);
		lookupTable->SetTableValue(0, 68 / 255.0, 1 / 255.0, 84 / 255.0);
		lookupTable->SetTableValue(1, 72 / 255.0, 31 / 255.0 / 255.0, 112 / 255.0);
		lookupTable->SetTableValue(2, 67 / 255.0, 63 / 255.0, 133 / 255.0);
		lookupTable->SetTableValue(3, 54 / 255.0, 92 / 255.0, 141 / 255.0);
		lookupTable->SetTableValue(4, 43 / 255.0, 117 / 255.0, 142 / 255.0);
		lookupTable->SetTableValue(5, 33 / 255.0, 142 / 255.0, 141 / 255.0);
		lookupTable->SetTableValue(6, 33 / 255.0, 166 / 255.0, 133 / 255.0);
		lookupTable->SetTableValue(7, 64 / 255.0, 189 / 255.0, 114 / 255.0);
		lookupTable->SetTableValue(8, 119 / 255.0, 209 / 255.0, 83 / 255.0);
		lookupTable->SetTableValue(9, 186 / 255.0, 222 / 255.0, 40 / 255.0);
		lookupTable->SetTableValue(10, 253 / 255.0, 231 / 255.0, 37 / 255.0);*/


		double range[2] = { 0, 0 };
		for (int i = 0; i < m_CellToPointGrids.size(); ++i)
		{
			auto pointData = m_CellToPointGrids.at(i)->GetOutput()->GetPointData();
			pointData->SetActiveScalars(field.toStdString().c_str());
			auto r = pointData->GetScalars()->GetRange();
			if (r[0] < range[0]) {
				range[0] = r[0];
			}
			if (r[1] > range[1]) {
				range[1] = r[1];
			}

		}

		for (int i = 0; i < m_CellToPointGrids.size(); ++i)
		{
			auto pointData = m_CellToPointGrids.at(i)->GetOutput()->GetPointData();
			pointData->SetActiveScalars(field.toStdString().c_str());

			m_Mappers.at(i)->ScalarVisibilityOn();
			m_Mappers.at(i)->SetLookupTable(lookupTable);
			m_Mappers.at(i)->SetScalarModeToUsePointFieldData();
			m_Mappers.at(i)->SetScalarRange(range);
			m_Mappers.at(i)->ColorByArrayComponent(pointData->GetScalars()->GetName(), 0);
		}

		if (m_ScalarBarActor != nullptr) {
			m_Renderer->RemoveActor(m_ScalarBarActor);
		}
		m_ScalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
		m_ScalarBarActor->SetOrientationToHorizontal();
		m_ScalarBarActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
		m_ScalarBarActor->SetPosition(0.1, 0.02);
		m_ScalarBarActor->SetWidth(0.8);
		m_ScalarBarActor->SetHeight(0.2);
		m_ScalarBarActor->SetLookupTable(lookupTable);
		vtkSmartPointer<vtkTextProperty> prop = vtkSmartPointer<vtkTextProperty>::New();
		//prop->SetFontFamily(1);
		//prop->SetFontSize(60);
		prop->SetColor(0, 0, 0);
		m_ScalarBarActor->SetTitleTextProperty(prop);
		m_ScalarBarActor->SetLabelTextProperty(prop);

		m_ScalarBarActor->SetTitle(field.toStdString().c_str());

		// vtkScalarBarWidget 无法对颜色条属性设置
		/*if (m_ScalarBarWidget == nullptr)
		{
			m_ScalarBarWidget = vtkSmartPointer<vtkScalarBarWidget>::New();
		}
		m_ScalarBarWidget->SetInteractor(m_RenderWindow->GetInteractor());
		m_ScalarBarWidget->SetScalarBarActor(scalarBarActor);
		m_ScalarBarWidget->On();*/
		m_CurrentShadeField = field;
		m_Renderer->AddActor(m_ScalarBarActor);
		m_RenderWindow->Render();
	}

	void C3fdRenderWidget::addStructuredGrid(QList<vtkSmartPointer<vtkStructuredGrid>> grids, QColor color, Common::DisplayMode mode)
	{
		for (auto g : grids)
		{
			addStructuredGrid(g, color, mode);
		}
	}

	void C3fdRenderWidget::addStructuredGrid(vtkSmartPointer<vtkStructuredGrid> grid, QColor color, Common::DisplayMode mode)
	{
		m_Grids.push_back(grid);

		vtkNew<vtkCellDataToPointData> c2p;
		c2p->SetInputData(grid);
		c2p->SetProcessAllArrays(true);
		// c2p->AddCellDataArray(name);
		c2p->PassCellDataOff();
		c2p->Update();
		m_CellToPointGrids.push_back(c2p);

		vtkNew<vtkDataSetMapper> mapper;
		mapper->SetInputData(c2p->GetOutput());
		m_Mappers.push_back(mapper);

		vtkNew<vtkActor> actor;
		actor->SetMapper(mapper);
		m_Actors.push_back(actor);
		switch (mode)
		{
		case Common::DisplayMode::Points:
			actor->GetProperty()->SetRepresentationToPoints();
			break;
		case Common::DisplayMode::Wireframe:
			actor->GetProperty()->SetRepresentationToWireframe();
			break;
		case Common::DisplayMode::Surface:
		default:
			actor->GetProperty()->SetRepresentationToSurface();
			break;
		}

		actor->GetProperty()->SetColor(color.redF(), color.greenF(), color.blueF());
		if (m_Renderer != nullptr)
		{
			m_Renderer->AddActor(actor);
		}
	}

	void C3fdRenderWidget::addPolyData(vtkPolyData *data, QColor color)
	{
		vtkNew<vtkDataSetMapper> mapper;
		mapper->SetInputData(data);
		m_Mappers.push_back(mapper);

		vtkNew<vtkActor> actor;
		actor->SetMapper(mapper);
		m_Actors.push_back(actor);
		actor->GetProperty()->SetRepresentationToSurface();
		actor->GetProperty()->SetColor(color.redF(), color.greenF(), color.blueF());
		if (m_Renderer != nullptr)
		{
			m_Renderer->AddActor(actor);
		}
	}

	void C3fdRenderWidget::setBackGroundColor(QColor color1, QColor color2)
	{
		m_Color1 = color1;
		m_Color2 = color2;
		if (m_Renderer != nullptr)
		{
			m_Renderer->SetBackground2(m_Color1.redF(), m_Color1.greenF(), m_Color1.blueF());
			m_Renderer->SetBackground(m_Color2.redF(), m_Color2.greenF(), m_Color2.blueF());
			m_Renderer->SetGradientBackground(true);
		}
	}

	void C3fdRenderWidget::setBackGroundColor(QColor color)
	{
		m_Color1 = color;
		m_Color2 = color;
		if (m_Renderer != nullptr)
		{
			m_Renderer->SetBackground(m_Color2.redF(), m_Color2.greenF(), m_Color2.blueF());
		}
	}

	void C3fdRenderWidget::setDisplayMode(Common::DisplayMode mode)
	{
		for (auto actor : m_Actors)
		{
			switch (mode)
			{
			case Common::DisplayMode::Points:
				actor->GetProperty()->SetRepresentationToPoints();
				break;
			case Common::DisplayMode::Wireframe:
				actor->GetProperty()->SetRepresentationToWireframe();
				break;
			case Common::DisplayMode::Surface:
			default:
				actor->GetProperty()->SetRepresentationToSurface();
				break;
			}
		}
	}

	void C3fdRenderWidget::addAxes()
	{
		if (m_RenderWindow != nullptr)
		{
			vtkNew<vtkAxesActor> axesActor;
			axesActor->SetPosition(0, 0, 0);
			axesActor->SetShaftType(0);
			axesActor->SetAxisLabels(1);
			axesActor->SetCylinderRadius(0.02);
			if (m_AxesWidget == nullptr)
			{
				m_AxesWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
			}
			m_AxesWidget->SetInteractor(m_RenderWindow->GetInteractor());
			m_AxesWidget->SetOutlineColor(0.9300, 0.5700, 0.1300);
			m_AxesWidget->SetOrientationMarker(axesActor);
			m_AxesWidget->SetViewport(0.0, 0.0, 0.1, 0.2);
			m_AxesWidget->SetEnabled(1);
			///禁止交互操作移动位置2
			m_AxesWidget->InteractiveOff();
		}
	}

	QString C3fdRenderWidget::getTitle()
	{
		return m_Title;
	}
	void C3fdRenderWidget::setTitle(QString title)
	{
		m_Title = title;
	}

	void C3fdRenderWidget::addTitle(QString title)
	{
		setTitle(title);
		if (m_RenderWindow != nullptr)
		{
			vtkSmartPointer<vtkTextActor> textActor = vtkSmartPointer<vtkTextActor>::New();
			textActor->SetInput(title.toStdString().c_str());
			textActor->GetTextProperty()->SetColor(0.8, 0.75, 0.75);
			textActor->GetTextProperty()->SetFontSize(15);
			textActor->SetDragable(false);

			if (m_TitleWidget == nullptr)
			{
				m_TitleWidget = vtkSmartPointer<vtkTextWidget>::New();
			}
			vtkSmartPointer<vtkTextRepresentation> textRepresentation = vtkSmartPointer<vtkTextRepresentation>::New();
			textRepresentation->GetPositionCoordinate()->SetValue(.02, 0.8);
			textRepresentation->GetPosition2Coordinate()->SetValue(.3, 0.1);
			m_TitleWidget->SetRepresentation(textRepresentation);

			m_TitleWidget->SetInteractor(m_RenderWindow->GetInteractor());
			m_TitleWidget->SetTextActor(textActor);
			// m_TitleWidget->SelectableOff();
			m_TitleWidget->On();
			m_TitleWidget->SelectableOff();
		}
	}

	void C3fdRenderWidget::setView(QString view)
	{
		if (view.toLower() == "fit")
			fitView();
		else if (view.toLower() == "xplus")
			setViewXPlus();
		else if (view.toLower() == "xminus")
			setViewXMiuns();
		else if (view.toLower() == "yplus")
			setViewYPlus();
		else if (view.toLower() == "yminus")
			setViewYMiuns();
		else if (view.toLower() == "zplus")
			setViewZPlus();
		else if (view.toLower() == "zminus")
			setViewZMiuns();
	}

	void C3fdRenderWidget::fitView()
	{
		if (m_Renderer != nullptr)
		{
			m_Renderer->ResetCamera();
			m_Renderer->GetRenderWindow()->Render();
		}
	}

	void C3fdRenderWidget::setViewXPlus()
	{
		if (m_Renderer != nullptr)
		{
			vtkCamera *camera = m_Renderer->GetActiveCamera();
			camera->SetViewUp(0, 0, 1);
			camera->SetPosition(5000, 0, 0);
			camera->SetFocalPoint(0, 0, 0);
			m_Renderer->ResetCamera();
			m_Renderer->GetRenderWindow()->Render();
		}
	}
	void C3fdRenderWidget::setViewXMiuns()
	{
		if (m_Renderer != nullptr)
		{
			vtkCamera *camera = m_Renderer->GetActiveCamera();
			camera->SetViewUp(0, 0, 1);
			camera->SetPosition(-5000, 0, 0);
			camera->SetFocalPoint(0, 0, 0);
			m_Renderer->ResetCamera();
			m_Renderer->GetRenderWindow()->Render();
		}
	}
	void C3fdRenderWidget::setViewYPlus()
	{
		if (m_Renderer != nullptr)
		{
			vtkCamera *camera = m_Renderer->GetActiveCamera();
			camera->SetViewUp(0, 0, 1);
			camera->SetPosition(0, 5000, 0);
			camera->SetFocalPoint(0, 0, 0);
			m_Renderer->ResetCamera();
			m_Renderer->GetRenderWindow()->Render();
		}
	}
	void C3fdRenderWidget::setViewYMiuns()
	{
		if (m_Renderer != nullptr)
		{
			vtkCamera *camera = m_Renderer->GetActiveCamera();
			camera->SetViewUp(0, 0, 1);
			camera->SetPosition(0, -5000, 0);
			camera->SetFocalPoint(0, 0, 0);
			m_Renderer->ResetCamera();
			m_Renderer->GetRenderWindow()->Render();
		}
	}
	void C3fdRenderWidget::setViewZPlus()
	{
		if (m_Renderer != nullptr)
		{
			vtkCamera *camera = m_Renderer->GetActiveCamera();
			camera->SetViewUp(0, 1, 0);
			camera->SetPosition(0, 0, 5000);
			camera->SetFocalPoint(0, 0, 0);
			m_Renderer->ResetCamera();
			m_Renderer->GetRenderWindow()->Render();
		}
	}
	void C3fdRenderWidget::setViewZMiuns()
	{
		if (m_Renderer != nullptr)
		{
			vtkCamera *camera = m_Renderer->GetActiveCamera();
			camera->SetViewUp(0, 1, 0);
			camera->SetPosition(0, 0, -1);
			camera->SetFocalPoint(0, 0, 0);
			m_Renderer->ResetCamera();
			m_Renderer->GetRenderWindow()->Render();
		}
	}

	void C3fdRenderWidget::refreshRender()
	{
		if (m_Renderer != nullptr)
		{
			m_Renderer->GetRenderWindow()->Render();
		}
	}

	void C3fdRenderWidget::enableFocusSignal(bool enable)
	{
		m_EnableFocusSignal = enable;
	}

	vtkSmartPointer<vtkRenderer> C3fdRenderWidget::getRenderer() const
	{
		return m_Renderer;
	}

	vtkSmartPointer<vtkRenderWindow> C3fdRenderWidget::getRenderWindow() const
	{
		return m_RenderWindow;
	}

	void C3fdRenderWidget::clearAllActor()
	{
		if (m_Renderer != nullptr) {
			auto *actors = m_Renderer->GetActors();
			vtkCollectionSimpleIterator actorIter;
			actors->InitTraversal(actorIter);
			vtkActor *actor = nullptr;
			while (actor = actors->GetNextActor(actorIter))
			{
				m_Renderer->RemoveActor(actor);
			}
		}
	}

	QMap<Common::FieldLocation, QList<QString>> C3fdRenderWidget::getFields()
	{
		if (m_Fields.isEmpty())
		{

			QList<QString> pointFields = {}, cellFields = {};
			for (vtkSmartPointer<vtkStructuredGrid> grid : m_Grids)
			{
				auto pointAttrs = grid->GetAttributes(vtkDataObject::POINT);
				auto numOfAttrs = pointAttrs->GetNumberOfArrays();
				for (int i = 0; i < numOfAttrs; i++)
				{
					auto name = pointAttrs->GetArrayName(i);
					if (!pointFields.contains(name))
					{
						pointFields.push_back(name);
					}
				}
				auto cellAttrs = grid->GetAttributes(vtkDataObject::CELL);
				numOfAttrs = cellAttrs->GetNumberOfArrays();
				for (int i = 0; i < numOfAttrs; i++)
				{
					auto name = cellAttrs->GetArrayName(i);
					if (!cellFields.contains(name))
					{
						cellFields.push_back(name);
					}
				}
			}

			m_Fields.insert(Common::FieldLocation::Point, pointFields);
			m_Fields.insert(Common::FieldLocation::Cell, cellFields);
		}
		return m_Fields;
	}

	void C3fdRenderWidget::saveImage(QString filePath, int width, int height, Common::AspectRatioMode mode, int quality)
	{
		int* screenSize = m_RenderWindow->GetScreenSize();
		if (width <= 0) {
			width = screenSize[0];
		}
		if (height <= 0) {
			height = screenSize[1];
		}
		switch (mode)
		{
		case Common::AspectRatioMode::KeepWidth:
			height = (int)(width * 1.000 / screenSize[0] * screenSize[1]);
			break;
		case Common::AspectRatioMode::KeepHeight:
			width = (int)(height * 1.000 / screenSize[1] * screenSize[0]);
			break;
		default:
			break;
		}

		if (!filePath.endsWith(".jpg")) {
			filePath += ".jpg";
		}
		if (quality < 0) {
			quality = 0;
		}
		else if (quality > 100) {
			quality = 100;
		}

		auto windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
		auto imageResize = vtkSmartPointer<vtkImageResize>::New();
		windowToImageFilter->SetInput(m_RenderWindow);
		imageResize->SetInputConnection(windowToImageFilter->GetOutputPort());
		imageResize->SetOutputDimensions(width, height, -1);
		imageResize->Update();
		auto jpegWriter = vtkSmartPointer<vtkJPEGWriter>::New();
		jpegWriter->SetQuality(quality);
		jpegWriter->SetFileName(filePath.toLocal8Bit().data());
		jpegWriter->SetInputConnection(imageResize->GetOutputPort());
		jpegWriter->Write();
	}

	QString C3fdRenderWidget::getShadeField() const
	{
		return m_CurrentShadeField;
	}

	bool C3fdRenderWidget::saveVtkFile(QString filename)
	{
		QFileInfo fileInfo(filename);
		auto suffix = fileInfo.suffix();
		if (suffix.isEmpty()) {
			suffix = "vtk";
		}
		int i = 0;

		if (m_Renderer != nullptr) {
			auto *actors = m_Renderer->GetActors();
			vtkCollectionSimpleIterator actorIter;
			actors->InitTraversal(actorIter);
			vtkActor *actor = nullptr;
			while (actor = actors->GetNextActor(actorIter))
			{
				vtkSmartPointer<vtkDataSetWriter> writer = vtkSmartPointer<vtkDataSetWriter>::New();
				writer->SetFileTypeToBinary();
				writer->SetInputData(actor->GetMapper()->GetInputAsDataSet());
				writer->SetFileName(QString("%1_%2.%3").arg(fileInfo.absolutePath() + QDir::separator() + fileInfo.completeBaseName()).arg(++i).arg(suffix).toStdString().c_str());
				writer->Write();
			}
		}

		/*
		for (vtkSmartPointer<vtkStructuredGrid> g : m_Grids) {
			vtkSmartPointer<vtkStructuredGridWriter> writer = vtkSmartPointer<vtkStructuredGridWriter>::New();
			writer->SetFileTypeToBinary();
			writer->SetInputData(g);
			writer->SetFileName(filename.replace(suffix, QString("_%1.%2").arg(++i).arg(suffix)).toStdString().c_str());
			writer->Write();
		}*/
		return true;
	}

	void C3fdRenderWidget::showBorderSlot(bool isShow)
	{
		if (!m_EnableFocusSignal) {
			return;
		}
		if (isShow)
		{
			emit focusInSig(this);
		}
		m_IsFocus = isShow;
		update();
	}

	void C3fdRenderWidget::focusInEvent(QFocusEvent *event)
	{
		if (!m_EnableFocusSignal) {
			return;
		}
		m_IsFocus = true;
		emit focusInSig(this);
		update();
	}

	void C3fdRenderWidget::focusOutEvent(QFocusEvent *event)
	{
		if (!m_EnableFocusSignal) {
			return;
		}
		m_IsFocus = false;
		update();
	}

	void C3fdRenderWidget::paintEvent(QPaintEvent *event)
	{

		if (m_EnableFocusSignal && m_IsFocus)
		{
			QPainter p(this);
			p.setPen(QColor("green"));
			p.drawRect(0, 0, width() - 2, height() - 2);
		}
		QWidget::paintEvent(event);
	}

}